#define _J_SINKRONIZACIJA_C_
#include "sinkronizacija.h"

#include "red.h"

#include <dretve.h>

#include "pozivi_jezgre.h"
#include "liste.h"
#include "razno.h"
#include "ispis.h"


ZAUZMI(static j_sinkro_t, j_sinkro, NAJ_SINKRO)

void j_inicijaliziraj_sinkronizaciju ()
{
	//sada je prazno, ali ...
}

int j_dohvati_sinkro (void *param)
{
	struct {
		uint8	tip;	//tip reda
		uint8	sort;	//nacin sortiranja reda
		uint16	zast;	//zastavice
		uint	poc_vr;	//pocetna vrijednost
	} *sinkro = param;
	int id_reda, id_reda2, id = -1;
	uint16 zast;
	int8 sort;
	
	PARAMNULL(sinkro);

	sort = sinkro->sort != 0 ? sinkro->sort : SORT_PRP;
	
	if (sort != SORT_PRP && sort != SORT_PRIO) {
		j_ispisi("Nepoznato uredjenje reda!\n");
		return -1;
	}

	zast = sinkro->zast;

	switch (sinkro->tip) {
	case MONITOR:
	case SEMAFOR:
	case BARIJERA:
	case CITAJPISI:
		if ((id = DOHVATI(j_sinkro)) == -1) {
			j_ispisi("Nema slobodnih sinkronizacijskih objekata\n");
			return -1;
		}
		//zast &= (REKURZ | NASPRIO);
		break;

	case REDUVJETA:
		zast = 0;
		break;
	
	default:
		j_ispisi("Nepoznati tip reda (%d)\n", sinkro->tip);
		return -1;
	}
	
	//sinkro struktura je dohvacena (ako je potrebna) => 'id'

	//dohvati red
	id_reda = j_dohvati_red(sinkro->tip, sort, zast);

	id_reda2 = 0;
	if (sinkro->tip == CITAJPISI)
		id_reda2 = j_dohvati_red(sinkro->tip, sort, zast);
	
	if (id_reda == -1 || id_reda2 == -1){
		OZNACI_SLOBODNO(j_sinkro, id);

		return -1; //nema slobodnog reda!
	}

	if (sinkro->tip == MONITOR || sinkro->tip == SEMAFOR ||
		sinkro->tip == BARIJERA || sinkro->tip == CITAJPISI)
	{
		//povezi red s objektom
		//j_red[id_reda].pr = id;
		j_dohv_red(id_reda)->pr = id;

		j_sinkro[id].id_dr = -1;

		//pocetna vrijednost
		if (sinkro->tip == MONITOR)
			j_sinkro[id].brojac = 1;
		else
			j_sinkro[id].brojac = sinkro->poc_vr;

		if (sinkro->tip == BARIJERA)
			j_sinkro[id].brojac2 = sinkro->poc_vr;
		else 
			j_sinkro[id].brojac2 = 0;

		if (sinkro->tip == CITAJPISI) {
			j_sinkro[id].red2 = id_reda2;
			j_dohv_red(id_reda2)->pr = -1;//ne koristiti!
		}
		else {
			j_sinkro[id].red2 = -1;
		}
	}

	return id_reda;
}

int j_obrisi_sinkro (void *param)
{
	int *pid = param, id, povr;
	j_red_t *red;
	
	PARAMNULL(pid);
	id = *pid;

	red = j_dohv_red(id);
	povr = j_obrisi_red(id);
	if (povr == -1)
		return -1;
	
	if (red->tip == MONITOR || red->tip == SEMAFOR ||
		red->tip == BARIJERA || red->tip == CITAJPISI)
	{
		if (OBRISI(j_sinkro, red->pr) == -1) {
			j_ispisi("Krivi id sinkro reda ili red nije zauzet\n");
			return -1;
		}
		if (j_sinkro[red->pr].red2 != -1) //red za pisace
			j_obrisi_red(j_sinkro[red->pr].red2);
	}
	return povr;
}

//Propusti_(sve)_iz_reda_uvjeta ili Postavi_OS ili Otkljucati_Monitor
int j_propusti_sinkro (void *param)
{
	struct {
		int id;		//id reda
		bool sve;	//propusta sve ili samo jednu dretvu?
	} *p = param;
	
	PARAMNULL(p);

	if (j_provjeri_sinkro(p->id) == -1)
		return -1;

	return j_propusti_sinkro_i(p->id, p->sve);
}

//interna, nakon provjere parametara (zove se i iz funkcija ispod)
int j_propusti_sinkro_i (int id_reda, bool sve)
{
	j_red_t *red = j_dohv_red(id_reda), *red2;
	j_sinkro_t *sinkro = &j_sinkro[red->pr];
	j_opisnikdretve_t *opd = j_aktivna_dretva();
	int povvr = 0;

	switch (red->tip) {
	case SEMAFOR:
	case MONITOR:
		if (sinkro->id_dr == opd->id && sinkro->brojac == 0 
			&& sinkro->brojac2 > 0)
		{
			//bilo je rekurzivnog poziva cekanja, samo smanji broj
			sinkro->brojac2--;
		}
		else if (POGLEDAJ_PRVI_ELEMENT(&red->red) != NULL) {
			//ima dretvi koje cekaju, propusti prvu
			povvr = j_propusti_iz_reda(id_reda, NEISTINA);
			sinkro->brojac2 = 0; //za svaki slucaj

			//nasljedjivanje prioriteta - provjeri
			j_vrati_prioritet(opd->id, sinkro);
		}
		else if (sinkro->brojac == 0 || red->tip == SEMAFOR) {
			//inace povecaj vrijednost semafora/monitora
			//ali ako je monitor vrijednost ne ide preko 1!
			sinkro->brojac++;

			//nasljedjivanje prioriteta - provjeri
			j_vrati_prioritet(opd->id, sinkro);
		}
		//else {} - brojac vec ima vrijednost 1, a radi se o monitoru
		
		break;

	case REDUVJETA:
		povvr = j_propusti_iz_reda(id_reda, sve);
		break;

	case BARIJERA://nema takve funkcije
		j_ispisi("Nema takve funkcije!\n");
		povvr = -1;
		break;

	case CITAJPISI:
		if (sinkro->brojac > 0)
			sinkro->brojac--;

		if (sinkro->brojac > 0)
			break;

		//ako ima pisaca, pusti prvo jednog od njih
		red2 = j_dohv_red(sinkro->red2);
		if (POGLEDAJ_PRVI_ELEMENT(&red2->red) != NULL) {
			j_propusti_iz_reda(sinkro->red2, NEISTINA);
			sinkro->brojac2 = PISAC;
			sinkro->brojac++;
		}
		else if (POGLEDAJ_PRVI_ELEMENT(&red->red) != NULL) {
			//pusti citace
			sinkro->brojac = j_propusti_iz_reda(id_reda, ISTINA);
			sinkro->brojac2 = CITAC;
		}
		else {	//nema nikog, oznaci kao slobodno
			sinkro->brojac2 = NITKO;
			sinkro->brojac = 0;
		}
		
		break;
	}
	
	return povvr;
}

//Uvrsti_u_red_uvjeta ili Cekaj_OS ili Zakljucaj_Monitor
int j_cekaj_sinkro (void *param)
{
	struct {
		int id_p;
		//primarni id (id reda uvjeta ili semafora ili monitora)

		uint zast;

		int id_s;
		//sekundarni id (ako je primarni red uvjeta ovo je monitor)

		vrijeme_t *koliko;
		//ako je != 0 kaze koliko ce se cekati u blokiranom redu
		//po isteku vremena dretva se odblokira, a povratna je
		//vrijednost -1 (oznaka greske)
	} *p = param;

	j_red_t *red, *sred;
	j_sinkro_t *sinkro;
	j_opisnikdretve_t *opd;
	int povvr = 0; //pretpostavka je da ce sve dobro proci
	
	PARAMNULL(p);

	if (j_provjeri_sinkro(p->id_p) == -1)
		return -1;

	//opisnik pozivajuce dretve
	opd = j_aktivna_dretva();

	red = j_dohv_red(p->id_p);

	switch (red->tip) {
	case REDUVJETA:
		//Uvrsti_u_red_uvjeta(id_reda = id_p, id_monitora = id_s)
		
		//ako ima drugi argument, on mora biti monitor ili semafor
		if (p->id_s != -1) {
			sred = j_dohv_red(p->id_s);
			if (sred == NULL || j_provjeri_sinkro(p->id_s) == -1 ||
				(sred->tip != MONITOR && sred->tip != SEMAFOR))
				return -1;

			//ako ima 2. argument postavi monitor/semafor
			povvr = j_propusti_sinkro_i(p->id_s, NEISTINA);
		}

		//stavi pozivajucu dretvu u red uvjeta
		j_stavi_u_red(p->id_p, opd, p->koliko);
	
		break;

	case MONITOR:
	case SEMAFOR:
		//Cekaj_OS ili Zakljucaj_Monitor
		
		sinkro = &j_sinkro[red->pr];

		if (sinkro->brojac > 0) { //prolazno
			sinkro->brojac--;
			sinkro->id_dr = opd->id;
			sinkro->brojac2 = 0; //resetiram
		}
		else if (sinkro->id_dr == opd->id && (red->zast & REKURZ)) {
			//nije prolazno, ali ova je dretva "vlasnik" objekta i
			//rekurzivni pozivi su dozvoljeni
			
			sinkro->brojac2++;
			
			povvr = 2;
		}
		else {
			//semafor/monitor je neprolazan - stavi dretvu u red

			//ako je zastavica PROBAJ postavljena samo vrati -1
			if (p->zast & PROBAJ) {
				povvr = -1;
				break;
			}
			
			j_stavi_u_red(p->id_p, opd, p->koliko);
			
			//nasljedjivanje prioriteta - poveca prioritet dretvi
			//koja "drzi" monitor/semafor
			if (sinkro->id_dr != -1 && (red->zast & NASPRIO)) {
				//ide i rekurzivno: ako ona koja drzi semafor
				//je blokirana u nekom trecem redu koji drzi
				//treca dretva - povecaj i njoj prioritet, itd.
				
				int prio = opd->prio;
				j_sinkro_t *s = sinkro;

				do {
					j_opisnikdretve_t *opd2;
					j_red_t *r;
					
					opd2 = j_dohvati_opisnik(s->id_dr);
					if (opd2 == NULL)
						break;

					if (opd2->prio <= prio)
						break;

					j_povecaj_prioritet(s->id_dr, prio, s);
					
					if (opd2->red == PRIPRAVNA ||
						opd2->red == AKTIVNA)
						break;

					r = j_dohv_red(opd2->red);

					if ((r->tip != MONITOR && 
						r->tip != SEMAFOR) || 
						!(r->zast & NASPRIO))
						break;

					s = &j_sinkro[r->pr];

				} while (1);
			}
		}
		break;

	case BARIJERA://cekaj_na_barijeri()
		sinkro = &j_sinkro[red->pr];

		sinkro->brojac--;

		if (sinkro->brojac > 0) { //blokiraj dretvu
			//ako je zastavica PROBAJ postavljena vrati -1
			if (p->zast & PROBAJ)
				povvr = -1;
			else
				j_stavi_u_red(p->id_p, opd, p->koliko);
		}
		else {//stigao i zadnji, pusti ih sve
			sinkro->brojac = sinkro->brojac2;
			j_propusti_iz_reda(p->id_p, ISTINA);
		}

		break;

	case CITAJPISI://zakljucaj_na_citanje/zakljucaj_na_pisanje
		sinkro = &j_sinkro[red->pr];

		//id_s - CITAC ili PISAC
		if (p->id_s != CITAC && p->id_s != PISAC) {
			j_ispisi("Greska u CITAC/PISAC zakljucavanju!\n");
			povvr = -1;
			break;
		}
		//blokiraj citace i ako pisac ceka!
		if (p->id_s == CITAC && (sinkro->brojac2 == PISAC ||
		POGLEDAJ_PRVI_ELEMENT(&(j_dohv_red(sinkro->red2)->red)) != NULL) )
		{
			//ako je zastavica PROBAJ postavljena vrati -1
			if (p->zast & PROBAJ)
				povvr = -1;
			else
				//blokiraj citaca
				j_stavi_u_red(p->id_p, opd, p->koliko);
		}
		else if (p->id_s == PISAC && sinkro->brojac2 != NITKO) {
			//ako je zastavica PROBAJ postavljena vrati -1
			if (p->zast & PROBAJ)
				povvr = -1;
			else
				//blokiraj pisaca
				j_stavi_u_red(sinkro->red2, opd, p->koliko);
		}
		else { //moze proc
			sinkro->id_dr = opd->id;
			sinkro->brojac2 = p->id_s;
			sinkro->brojac++;
		}

		break;

	default:
		j_ispisi("Krivi tip primarnog reda (nije SEMAFOR, MONITOR, "
				"REDUVJETA, ...)\n");
		povvr = -1;
		break;
	}

	return povvr;
}

//pomocna funkcija
int j_provjeri_sinkro(int id_reda)
{
	int id_sinkro;
	j_red_t *red = j_dohv_red(id_reda);
	
	if (red == NULL)
		return -1;

	switch (red->tip) {
	case MONITOR:
	case SEMAFOR:
	case BARIJERA:
	case CITAJPISI:
		id_sinkro = red->pr;
		
		if (id_sinkro < 0 || id_sinkro >= NAJ_SINKRO || 
			PROVJERI_SLOBODNO(j_sinkro, id_sinkro))
		{
			j_ispisi("Krivi id sinkronizacijskog objekta (mozda "
				"red nije sinkronizacijski ili nije zauzet)\n");
			return -1;
		}
		break;

	case REDUVJETA:
		break;
	
	default:
		j_ispisi("Nepoznati tip reda (nije SEMAFOR, MONITOR, "
				"REDUVJETA)\n");
		return -1;
	}

	return 0;
}
